
#ifndef __QTWI_ITHREAD_HPP
#define __QTWI_ITHREAD_HPP
	
#include <typeinfo>
#include <map>

#include "common.hpp"
#include "iapplication.hpp"

#include <cstdlib>
#include <cstring>

extern "C" {
#include <pthread.h>
}

template <class T, int attr_type = PTHREAD_MUTEX_NORMAL>
class IMutexLock
{
private:
	T &var;
	mutable pthread_mutex_t mutex;
	mutable bool locked;
public:
	class_iexception(IMutexLockFailureLocking, "mutex locking failed");
	class_iexception(IMutexLockFailureRelease, "mutex unlocking failed");
	class_iexception(IMutexNonLockedAccess, "mutex not locked for data access");
	class_iexception(IMutexAttributeSet, "cannot set mutex attribute");
	
	IMutexLock(volatile T &v) : var(const_cast<T&>(v)), locked(false)
	{
		memset(&mutex, 0, sizeof mutex);


		pthread_mutexattr_t attr;

		if(pthread_mutexattr_init(&attr) != 0 ||
		   pthread_mutexattr_settype(&attr, attr_type) != 0 ||
		   pthread_mutex_init(&mutex, &attr) != 0)
			throw IMutexAttributeSet();
	}

	bool& isLocked()
	{
		return locked;
	}

	void aquire() const
	{
		if(pthread_mutex_lock(&mutex) != 0) {
#ifdef DEBUG
			perror("pthread_mutex_lock");
#endif
			throw IMutexLockFailureLocking();
			
		}
		
		locked = true;
	}

	void release() const
	{
		if(pthread_mutex_unlock(&mutex) != 0)
			throw IMutexLockFailureRelease();
		locked = false;
	}

	// Lock, copy, release
	void set(T v)
	{
		aquire();
		var = v;
		release();
	}

	// Lock, make copy, unlock, return
	T read() const
	{
		T v;
		aquire();
		v = var;
		release();
		return v;
	}

	void unchecked_st(T v)
	{
		var = v;
	}

	T& unchecked_read()
	{
		return var;
	}
	
	T* operator->()
	{
		aqr_check();
		return &var;
	}

	T& operator*()
	{
		aqr_check();
		return var; 
	}

private:
	void aqr_check() throw(IMutexNonLockedAccess)
	{
		if(!locked) throw IMutexNonLockedAccess();
	}
};


class IThread {

private:
	// Thread data for static thread functions used by all IThreads
	class IThreadData
	{
	private:
		volatile bool running;
		IThread &t;
		void *data;
		IMutexLock<bool> running_lock;
	public:
		IThreadData(IThread &it, void *d) : running(false),  t(it), data(d), running_lock(running) {}

		IThread& getIThread() {
			return t;
		}

		void* operator*() {
			return data;
		}

		void setRunning(bool r) {
			running = r;
		}

		bool isRunning() {
			return running;
		}
	};
	
	pthread_t thread;
	IThreadData data;
	
	// Function called by all IThreads; does dispatching to single IThread instances function handlers
	static void* generic_ithread_func(void *ithread_data);
	
public:

	// Exception
	class_iexception(IThreadCreationError, "could not create thread");
	class_iexception(IThreadUnjoinable, "could not join thread");

	// Interface
	IThread(void *usr_data) : data(*this, usr_data) { }

	IThread() : data(*this, NULL) { }
	
	pthread_t* getThread()
	{
		return & thread;
	}
	
	// start thread
	void start();

	// stop thread
	void stop();

	void join();

	// user has to check this
	bool isRunning() {
		return data.isRunning();
	}
	
	// called thread action function receiving user data; to be reimplemented by any user derivation
	virtual void action(void *) = 0;

};

#endif
